﻿using System;
using dnAnalytics.LinearAlgebra;
using dnAnalytics.LinearAlgebra.Decomposition;
using NUnit.Framework;

namespace dnAnalytics.Tests.LinearAlgebra.Decomposition
{
    public abstract class CholeskyTests
    {
        private Cholesky mCholesky;
        private Matrix mMatrix;
        private Matrix mNotPDMatrix;
        private Cholesky mNotPostiveDefinite;

        protected abstract Matrix GetMatrix(string file);
        protected abstract Matrix GetMatrix(int size);
        protected abstract Matrix GetMatrix(int rows, int columns);
        protected abstract Matrix GetIdentityMatrix(int size);
        protected abstract Vector GetVector(int size);

        [TestFixtureSetUp]
        public void SetUp()
        {
            mMatrix = GetMatrix("./TestData/Matrices/hilbert_real_symmetric_array_10.mtx");
            mCholesky = new Cholesky(mMatrix);
            mNotPDMatrix = GetMatrix("./TestData/Matrices/gear_integer_general_coordinate_100.mtx");
            mNotPostiveDefinite = new Cholesky(mNotPDMatrix);
        }

        [Test]
        [ExpectedException(typeof (ArgumentNullException))]
        public void CholeskyConstructorNull()
        {
            Cholesky cholesky = new Cholesky(null);
        }

        [Test]
        [ExpectedException(typeof (MatrixNotSquareException))]
        public void CholeskyConstructor()
        {
            Matrix matrix = GetMatrix(3, 2);
            Cholesky cholesky = new Cholesky(matrix);
        }

        [Test]
        public void PositiveDefinite()
        {
            Assert.IsTrue(mCholesky.IsPositiveDefinite());
        }

        [Test]
        public void NotPositiveDefinite()
        {
            Assert.IsFalse(mNotPostiveDefinite.IsPositiveDefinite());
        }

        [Test]
        public void Factor()
        {
            Matrix factor = mCholesky.Factor();
            Matrix result = factor*factor.Transpose();

            for (int i = 0; i < result.Rows; i++)
            {
                for (int j = 0; j < result.Columns; j++)
                {
                    double error;
                    if (mMatrix[i, j] != 0)
                    {
                        error = System.Math.Abs((result[i, j] - mMatrix[i, j])/mMatrix[i, j]);
                    }
                    else
                    {
                        error = System.Math.Abs(result[i, j] - mMatrix[i, j]);
                    }

                    Assert.IsTrue(error < Constants.AcceptableError);
                }
            }
        }

        [Test]
        public void Determinant()
        {
            double expected = 2.183E-53;
            double actual = mCholesky.Determinant();
            Assert.AreEqual(expected, actual, Constants.IOAcceptableError);
        }
    }
}